package com.ingenico.insider.services.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.MissingResourceException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

import com.ingenico.insider.nodes.PSampleChannel;
import com.ingenico.insider.swing.CurveCloseAction;
import com.ingenico.insider.swing.CurveLoadAction;
import com.ingenico.insider.swing.CurveOnTopAction;
import com.ingenico.insider.swing.CurveSaveAction;
import com.ingenico.insider.swing.CurveSaveAsAction;
import com.ingenico.insider.swing.JRawFormatChooser;
import com.ingenico.insider.util.Constants;

public class CurvesControlSupplier {
	private static final Logger _logger = Logger.getLogger(CurvesControlSupplier.class.getCanonicalName());

	/**
	 * Public constant keywords
	 */
	public static final String CURVE_KEY = "curve";
	public static final String CURVE_LOAD_PATH   = CURVE_KEY + Constants.SEPARATOR + "load";
	public static final String CURVE_SAVE_PATH   = CURVE_KEY + Constants.SEPARATOR + "save";
	public static final String CURVE_SAVEAS_PATH = CURVE_KEY + Constants.SEPARATOR + "saveas";
	public static final String CURVE_CLOSE_PATH  = CURVE_KEY + Constants.SEPARATOR + "close";
	public static final String CURVE_ONTOP_PATH  = CURVE_KEY + Constants.SEPARATOR + "ontop";

	public static final String OVERWRITE_MESSAGE_KEY = "overwrite";
	public static final String DEFAULT_CURVE_PATH_KEY = "default.path";

	/**
	 * singleton instance
	 */
	private static CurvesControlSupplier instance;

	/**
	 * The Layout Actions used with menu items and buttons
	 */
	private final Action loadCurveAction;
	private final Action saveCurveAction;
	private final Action saveAsCurveAction;
	private final Action closeCurveAction;
	private final Action onTopCurveAction;


	/**
	 * The file chooser used to load and save curves...
	 */
	private final JFileChooser fileChooser;
	
	private final JRawFormatChooser rawFormatChooser;

	private final FileNameExtensionFilter auFilter;

	/**
	 * The JList graphical component that will display the curves list
	 */
	private final JList curvesList;

	private void initFileChooser() {
		String resourceKey;
		String resourceString;

		// Setting up default path from resource bundle data
		resourceKey = Constants.INSIDER_BASE + Constants.SEPARATOR + CURVE_KEY + Constants.SEPARATOR + DEFAULT_CURVE_PATH_KEY;
		try {
			resourceString = UserPreferencesSupplier.getInstance().getString(resourceKey);
			if ( ! resourceString.isEmpty()) {
				final File defaultCurvePath = new File(resourceString);
				if (defaultCurvePath.isDirectory()) {
					fileChooser.setCurrentDirectory(defaultCurvePath);
				} else {
					_logger.warning("Resource \"" + resourceKey + "\"=\"" + resourceString + "\" does not point to an actual directory. Cannot set default curve path.");
				}
			} else {
				_logger.info("Resource \"" + resourceKey + "\" is empty. Cannot set default curve path.");
			}
		} catch (MissingResourceException mre) {
			_logger.info("Resource \"" + resourceKey + "\" is missing. Cannot set default curve path.");
		}

		// Enable to select multiple files at once.
		fileChooser.setMultiSelectionEnabled(true);

		// Add file filter based on file extension
		fileChooser.addChoosableFileFilter(auFilter);
		fileChooser.addChoosableFileFilter(fileChooser.getAcceptAllFileFilter());
	}

	private void initMenu() {
		JMenuItem currentMenuItem;
		Action currentAction;
		JMenu currentMenu;
		Action[] actions;

		currentMenu = StandardMenuSupplier.getInstance().getFileMenu();
		actions = new Action[] {
			loadCurveAction,
			saveCurveAction,
			saveAsCurveAction,
			closeCurveAction
		};

		// Loop to init all file menu items at once and register them with a single action handler
		for (int i = 0; i < actions.length; i++) {
			currentAction = actions[i];
			currentMenuItem = new JMenuItem();
			currentMenuItem.setAction(currentAction);
			currentMenu.add(currentMenuItem, i);
		}
		
		currentMenu = StandardMenuSupplier.getInstance().getEditMenu();
		actions = new Action[] {
			onTopCurveAction
		};

		// Loop to init all file menu items at once and register them with a single action handler
		for (int i = 0; i < actions.length; i++) {
			currentAction = actions[i];
			currentMenuItem = new JMenuItem();
			currentMenuItem.setAction(currentAction);
			currentMenu.add(currentMenuItem, i);
		}		
	}

	private void initCurvesList() {
		curvesList.setModel(CurvesSupplier.getInstance().getModel());
		curvesList.addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				final boolean selectionAvailable = (curvesList.getSelectedValue() != null);

				closeCurveAction.setEnabled(selectionAvailable);
				onTopCurveAction.setEnabled(selectionAvailable);
			}
		});
	}

	/**
	 * private constructor of final class to prevent external instantiation
	 */
	private CurvesControlSupplier() {
		// Create actions
		loadCurveAction = (CurveLoadAction) LocalisationSupplier.getInstance().localize(new CurveLoadAction(), CURVE_LOAD_PATH);
		saveCurveAction = (CurveSaveAction) LocalisationSupplier.getInstance().localize(new CurveSaveAction(), CURVE_SAVE_PATH);
		saveAsCurveAction = (CurveSaveAsAction) LocalisationSupplier.getInstance().localize(new CurveSaveAsAction(), CURVE_SAVEAS_PATH);
		closeCurveAction = (CurveCloseAction) LocalisationSupplier.getInstance().localize(new CurveCloseAction(), CURVE_CLOSE_PATH);
		saveCurveAction.setEnabled(false);
		saveAsCurveAction.setEnabled(false);
		closeCurveAction.setEnabled(false);

		onTopCurveAction = (CurveOnTopAction) LocalisationSupplier.getInstance().localize(new CurveOnTopAction(), CURVE_ONTOP_PATH);
		onTopCurveAction.setEnabled(false);

		// Create filters for the file chooser
		auFilter  = new FileNameExtensionFilter("AU Files (.au)", "au");

		// Creates the files chooser once for all and initializes it
		fileChooser = new JFileChooser();
		initFileChooser();
		
		rawFormatChooser = new JRawFormatChooser();

		curvesList = new JList();
		initCurvesList();

		initMenu();

		_logger.fine("Registering curves list to the dock framework");
		DockableViewsSupplier.getInstance().add(new JScrollPane(curvesList), CURVE_KEY);
	}

	/**
	 * retrieve the singleton instance
	 *
	 * @return the CurvesControlSupplier singleton
	 */
	public static CurvesControlSupplier getInstance() {
		if (instance == null) {
			_logger.info(CurvesControlSupplier.class.getName() + " instance does not exist... Creating instance.");
			instance = new CurvesControlSupplier();
		}
		return instance;
	}

	public void putOnTop() {
		Object [] selectedItems = curvesList.getSelectedValues();
		if (selectedItems != null) {
			for (Object selectedCurve : selectedItems) {
				CurvesSupplier.getInstance().getModel().removeElement((PSampleChannel)selectedCurve);
				((PSampleChannel)selectedCurve).moveToFront();
				CurvesSupplier.getInstance().getModel().addElement((PSampleChannel)selectedCurve);
//TODO: put selected curves on top and modify the list....				
//				CurvesSupplier.getInstance().close((PSampleChannel)selectedCurve);
			}
		}
	}

	public void load() {
		int status = fileChooser.showOpenDialog(DockableViewsSupplier.getInstance().getComponent());
		if (status == JFileChooser.APPROVE_OPTION) {
//			final File selectedFile = fileChooser.getSelectedFile();
			final FileFilter selectedFilter = fileChooser.getFileFilter();

			final File selectedFiles[] = fileChooser.getSelectedFiles();
			for (final File selectedFile : selectedFiles) {
				try {
					if (selectedFilter == fileChooser.getAcceptAllFileFilter()) {
						_logger.fine("Default selection filter");
						if (rawFormatChooser.isInteractionDisabled()) {
							CurvesSupplier.getInstance().loadRawFile(selectedFile, rawFormatChooser.getFormat(), rawFormatChooser.getSign(), rawFormatChooser.getOrder(), rawFormatChooser.getSampleRate());
						} else {
							status = rawFormatChooser.showRawFormatDialog(DockableViewsSupplier.getInstance().getComponent(), selectedFile.getName());
							if (status == JOptionPane.OK_OPTION) {
								CurvesSupplier.getInstance().loadRawFile(selectedFile, rawFormatChooser.getFormat(), rawFormatChooser.getSign(), rawFormatChooser.getOrder(), rawFormatChooser.getSampleRate());
							} else {
								break;
							}
						}
					} else if (selectedFilter == auFilter) {
						_logger.fine("AU file selection filter");
						CurvesSupplier.getInstance().loadAuFile(selectedFile);
					} else {
						_logger.severe("Unknown Curve File Filter");
						MessageBoxSupplier.getInstance().showErrorDialog(CurvesSupplier.NOTFOUND_MESSAGE_KEY, selectedFile);
					}
				} catch (FileNotFoundException fnfe) {
					_logger.log(Level.WARNING, fnfe.getMessage(), fnfe);
					MessageBoxSupplier.getInstance().showErrorDialog(CurvesSupplier.NOTFOUND_MESSAGE_KEY, selectedFile);
				} catch (IOException ioe) {
					_logger.log(Level.SEVERE, ioe.getMessage(), ioe);
					MessageBoxSupplier.getInstance().showErrorDialog(CurvesSupplier.IO_MESSAGE_KEY, ioe);
				}
			}
			// Reset the user interaction flag otherwise the user will never be asked again for a format...
			rawFormatChooser.setInteractionDisabled(false);
		}
	}

	public void save() {
		final File selectedFile = fileChooser.getSelectedFile(); 
		if (selectedFile != null) {
			try {
				CurvesSupplier.getInstance().save(selectedFile);
			} catch (IOException ioe) {
				_logger.log(Level.SEVERE, ioe.getMessage(), ioe);
				MessageBoxSupplier.getInstance().showErrorDialog(CurvesSupplier.IO_MESSAGE_KEY, ioe);
			}
		} else {
			saveAs();
		}
	}

	public void saveAs() {
		boolean isInteracting = true;
		boolean fileChoosen = false;
		int status;

		do {
			// Prompt the user for a file
			status = fileChooser.showSaveDialog(DockableViewsSupplier.getInstance().getComponent());
			switch (status) {
			case JFileChooser.APPROVE_OPTION:
				final File selectedFile = fileChooser.getSelectedFile();

				if (selectedFile.exists()) {
					status = MessageBoxSupplier.getInstance().showConfirmDialog(OVERWRITE_MESSAGE_KEY, selectedFile);
					switch (status) {
					case JOptionPane.YES_OPTION:
						// The user accepts to overwrite the file
						fileChoosen = true;
						isInteracting = false;
						break;
					case JOptionPane.NO_OPTION:
						// The user do not want to overwrite the file but did not cancel
						isInteracting = true;
						break;
					default:
						isInteracting = false;
					}
				} else {
					fileChoosen = true;
					isInteracting = false;
				}
				if (fileChoosen) {
					try {
						CurvesSupplier.getInstance().save(selectedFile);
					} catch (IOException ioe) {
						_logger.log(Level.SEVERE, ioe.getMessage(), ioe);
						MessageBoxSupplier.getInstance().showErrorDialog(CurvesSupplier.IO_MESSAGE_KEY, ioe);
					}
					saveCurveAction.setEnabled(true);
				}
				break;
			default:
				isInteracting = false;
			}
		} while (isInteracting);
	}

	public void close() {
//TODO: implement confirm dialog box if dirty etc...
		Object [] selectedItems = curvesList.getSelectedValues();
		if (selectedItems != null) {
			for (Object selectedCurve : selectedItems) {
				CurvesSupplier.getInstance().close((PSampleChannel)selectedCurve);
			}
		}
	}

	public void addListSelectionListener(ListSelectionListener listener) {
		curvesList.addListSelectionListener(listener);
	}

	public Object[] getSelectedValues() {
		return curvesList.getSelectedValues();
	}

	public PSampleChannel getSelectedValue() {
		return (PSampleChannel)curvesList.getSelectedValue();
	}
}
